﻿#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    // UI 初始化
    ui->setupUi(this);

    // 初始化消息对话框
    this->MsgDlg = new class QMessageBox();

    // New从机文件
    this->SlaveFile = new QFile();

    // 初始化SlaveTable
    ui->SlaveTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    //ui->SlaveTable->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);

    // 进度条初始化
    ui->ProgressBar->setRange(0,100);
    ui->ProgressBar->reset();

    // 初始化
    this->u8SlaveNums = 0;      // 从机数量初始化为0
    this->strSaveFilePath = "/";// 合成文件保存位置初始化为根目录
    this->strSlavePath = "/";   // 从机文件加载位置初始化为根目录

    // 创建子机信息
    this->SlaveInfor = new class SlaveInfor();

    // 连接信号槽
    connect(this->SlaveInfor,
            SIGNAL(SendSlaveInfor(QString,QString)),
            this,
            SLOT(RecvSlaveInfor(QString,QString)));
    // 显示从机数量
    ui->LineEdit_SlaveNums->setText(QString::number((int)this->u8SlaveNums));
    ui->LineEdit_SlaveNums->setEnabled(FALSE);
    // 保存路径禁止编辑
    ui->LineEdit_SavePath->setFocusPolicy(Qt::NoFocus);
}

Widget::~Widget()
{
    delete ui;
}

// 检查整机编码是否合法，整机编码只能是数字、字母、下划线组合
bool Widget:: CheckMachineCodeIsLegal(QString strMachineCode)
{
    bool bLegal = true;
    int i=0;

    if(strMachineCode.isEmpty() || strMachineCode.isNull())
    {
        return  false;
    }

    // 遍历strSWLable
    while(i < strMachineCode.length())
    {
        if((strMachineCode.at(i) >= '0' && strMachineCode.at(i) <= '9')
            || (strMachineCode.at(i) >= 'A' && strMachineCode.at(i) <= 'Z')
                || (strMachineCode.at(i) >= 'a' && strMachineCode.at(i) <= 'z')
                    || strMachineCode.at(i) == '_')
        {
            i++;
            continue;
        }
        else
        {
            bLegal = false;
            break;
        }
    }

    return bLegal;
}

// 检查整机版本号是否合法，整机版本号只能是数字，长度为14个字符
bool Widget:: CheckMachineVerIsLegal(QString strMachineVer)
{
    bool bLegal = true;
    int i=0;

    if(strMachineVer.size() != 14)
    {
        return false;
    }

    // 遍历strMachineVer
    while(i < strMachineVer.length())
    {
        if(strMachineVer.at(i) > '9' || strMachineVer.at(i) < '0')
        {
            bLegal = false;
            break;
        }
        i++;
    }

    return bLegal;
}

// 查询子机信息表是否非法
bool Widget::CheckSlaveTableIsLegal()
{
    bool bLegal = true;
    int i=0;

    // 获取子机信息表的行数
    int RowNums = ui->SlaveTable->rowCount();

    // 按行顺序遍历子机信息表
    while(i < RowNums)
    {
        if(ui->SlaveTable->item(i,1) == 0 || ui->SlaveTable->item(i,2) == 0)
        {
            bLegal = false;
            break;
        }
        else
        {
            i++;
        }
    }
    return bLegal;
}

// 子机信息接收槽函数
void Widget::RecvSlaveInfor(QString strSWLable,QString strSWVer)
{
    qDebug()<<"RecvSlaveInfor";
    // 获取选中的行
    QList<QTableWidgetSelectionRange>SleteRanges = ui->SlaveTable->selectedRanges();

    // 没有选中行，提前退出
    if(SleteRanges.count() == 0)
    {
        return;
    }

    // 获取选中的行号
    int RowNum = SleteRanges.at(0).topRow();

    // 显示软件标识符
    QTableWidgetItem *SlaveSWLable = new QTableWidgetItem(strSWLable);
    SlaveSWLable->setTextAlignment(Qt::AlignCenter);// 居中对齐
    ui->SlaveTable->setItem(RowNum,1,SlaveSWLable);

    // 显示版本号
    QTableWidgetItem *SlaveSWVer = new QTableWidgetItem(strSWVer);
    SlaveSWVer->setTextAlignment(Qt::AlignCenter);// 居中对齐
    ui->SlaveTable->setItem(RowNum,2,SlaveSWVer);
}

// 修改按键槽函数
void Widget::on_Btn_Change_clicked()
{
    // 获取选中的行
    QList<QTableWidgetSelectionRange>SleteRanges = ui->SlaveTable->selectedRanges();

    // 没有选中行，提前退出
    if(SleteRanges.count() == 0)
    {
        return;
    }

    // 获取选中的行号
    int RowNum = SleteRanges.at(0).topRow();

    // 获取选中行的子机文件名称
    if(ui->SlaveTable->item(RowNum,0) != 0)
    {
       QString strSlaveFileName = ui->SlaveTable->item(RowNum,0)->text();
       this->SlaveInfor->SlaveInfor_SetSlaveName(strSlaveFileName);
    }

    // 获取选中行的子机软件标识符
    if(ui->SlaveTable->item(RowNum,1) != 0)
    {
        QString strSlaveSWLable = ui->SlaveTable->item(RowNum,1)->text();
        this->SlaveInfor->SlaveInfor_SetSlaveLable(strSlaveSWLable);
    }
    else
    {
        this->SlaveInfor->SlaveInfor_ClearSlaveSWLable();
    }

    // 获取选中行的子机版本号
    if(ui->SlaveTable->item(RowNum,2) != 0)
    {
        QString strSlaveSWVer = ui->SlaveTable->item(RowNum,2)->text();
        this->SlaveInfor->SlaveInfor_SetSlaveVer(strSlaveSWVer);
    }
    else
    {
        this->SlaveInfor->SlaveInfor_ClearSlaveSWVer();
    }

    // 获取选中行的子机号
    if(ui->SlaveTable->item(RowNum,3) != 0)
    {
        QString strSlaveNum = ui->SlaveTable->item(RowNum,3)->text();
        this->SlaveInfor->SlaveInfor_SetSlaveNum(strSlaveNum);
    }

    // 弹框显示子机信息
    this->SlaveInfor->show();

    qDebug()<< "SleteRanges:%d\r\n "<<RowNum;
}

// “删除”按键槽函数
void Widget::on_Btn_Del_clicked()
{
    // 获取选中的行
    QList<QTableWidgetSelectionRange>SleteRanges=ui->SlaveTable->selectedRanges();

    // 没有选中行，提前退出
    if(SleteRanges.count() == 0)
    {
        return;
    }

    // 获取选中的行号
    int RowNum = SleteRanges.at(0).topRow();

    // 删除选中行
    ui->SlaveTable->removeRow(RowNum);

    // 更新子机数量
    this->u8SlaveNums--;
    this->ui->LineEdit_SlaveNums->setText(QString::number((int)this->u8SlaveNums));

    // 更新子机号
    for(int i=0;i<ui->SlaveTable->rowCount();i++)
    {
        QString strSlaveNum = QString::number(i);
        QTableWidgetItem *SlaveNum =  new QTableWidgetItem(strSlaveNum);
        SlaveNum->setTextAlignment(Qt::AlignCenter);// 居中对齐
        ui->SlaveTable->setItem(i,3,SlaveNum);
    }
}

// “重置”按键槽函数
void Widget::on_Btn_Reset_clicked()
{
    // 清除子机信息表
    this->on_Btn_Clear_clicked();

    // 清除整机信息
    this->ui->LineEdit_MachineCode->clear();
    this->ui->LineEdit_MachineVer->clear();
    this->ui->LineEdit_SavePath->clear();

    // 复位进度条
    ui->ProgressBar->reset();
}

// “保存目录”按键槽函数
void Widget::on_Path_clicked()
{
    if(this->strSaveFilePath.isEmpty())
    {
        this->strSaveFilePath = "/";
    }

    QString strMachineCode = ui->LineEdit_MachineCode->text();
    QString strMachineVer = ui->LineEdit_MachineVer->text();

    // this->strSaveFilePath = "/" 且整机编码和版本号合法，合成默认bin文件文件名
    if(this->strSaveFilePath == "/" && this->CheckMachineCodeIsLegal(strMachineCode) && this->CheckMachineVerIsLegal(strMachineVer))
    {
        strMachineCode += "_v";

        QString strMajorVer = QString::number(strMachineVer.mid(0,2).toInt());

        QString strMinorVer = "."+QString::number(strMachineVer.mid(2,2).toInt());

        QString strRevisionVer = "."+QString::number(strMachineVer.mid(4,2).toInt())+"_";

        QString strData = strMachineVer.mid(6,8);

        this->strSaveFilePath += strMachineCode+strMajorVer+strMinorVer+strRevisionVer+strData;

        qDebug()<<"FilePathi Init:"<<this->strSaveFilePath;
    }

    this->strSaveFilePath = this->cSaveFileDialog.getSaveFileName(this,
                                                               "保存位置",
                                                               this->strSaveFilePath,
                                                               "二进制文件(*.bin)");
    ui->LineEdit_SavePath->setText(strSaveFilePath);

    qDebug()<<"文件保存位置："<<strSaveFilePath;
}

// “清空”按键槽函数
void Widget::on_Btn_Clear_clicked()
{
    // 删除全部行
    while(ui->SlaveTable->rowCount() != 0)
    {
        ui->SlaveTable->removeRow(ui->SlaveTable->rowCount()-1);
    }

    // 子机数量清零
    this->u8SlaveNums = 0;
    ui->LineEdit_SlaveNums->setText(QString::number((int)this->u8SlaveNums));
}

// 添加按键槽函数
void Widget::on_Btn_AddSlave_clicked()
{
    // 加载从机bin文件
    this->strSlavePath = this->cSlaveFileDialog.getOpenFileName(this,
                                                                "添加从机",
                                                                this->strSlavePath,
                                                                "二进制文件(*.bin)");
    qDebug()<<"从机bin文件位置："<<strSlavePath;

    if(this->strSlavePath.isEmpty())
    {
        // 文件路径为空，提前退出
        return;
    }

    // 将从机地址加入到容器，打包的时候需要使用
    this->SlavePathList.append(this->strSlavePath);

    // 若是本组生成的bin文件，要从Header信息中查找软件标识符和版本号
    QFile SlaveFile(this->strSlavePath);
    QDataStream SlaveFileOut(&SlaveFile);

    if(!SlaveFile.open(QIODevice::ReadOnly))
    {
        // 文件打开失败
        qDebug()<<"文件打开失败";
        this->MsgDlg->critical(this,
                               "文件打开失败",
                               "文件打开失败，请检查从机文件是否损坏");
        return ;
    }

    // 新建一行
    ui->SlaveTable->insertRow(this->u8SlaveNums);

    // 获取文件名
    QString strSlaveName = this->strSlavePath.mid(this->strSlavePath.lastIndexOf('/')+1);
    QTableWidgetItem *SlaveName = new QTableWidgetItem(strSlaveName);
    SlaveName->setTextAlignment(Qt::AlignCenter);// 居中对齐
    ui->SlaveTable->setItem(ui->SlaveTable->rowCount()-1,0,SlaveName);

    // 获取子机号
    QString strSlaveNum = QString::number((int)this->u8SlaveNums);
    QTableWidgetItem *SlaveNum =  new QTableWidgetItem(strSlaveNum);
    SlaveNum->setTextAlignment(Qt::AlignCenter);// 居中对齐
    ui->SlaveTable->setItem(ui->SlaveTable->rowCount()-1,3,SlaveNum);

    // 读取ImageHeader,先将前4K数据和ImageHeader全部读出来
    U8 *pu8FileData = new U8[U16_IMAGEHEADER_OFFSET + U8_IMAGEHEADER_SIZE];
    SlaveFileOut.readRawData((char *)pu8FileData,U16_IMAGEHEADER_OFFSET + U8_IMAGEHEADER_SIZE);
    // 找到ImageHeader
    ImageHeader_ts *psImageHeader = (ImageHeader_ts *)(pu8FileData + U16_IMAGEHEADER_OFFSET);

    U16 CRC  = CRC_CalcValue((U8*)psImageHeader,(U32)(U8_IMAGEHEADER_SIZE-2),u16CRC_SEED);
    // 校验Header
    if(psImageHeader->u16ImageHeaderCRC == CRC)
    {
        // 校验通过，获取子机编码
        QString strSlaveSWLable = (const char *)psImageHeader->au8ImageId;

        // 获取子机版本号
        QString strSwMajorVersion("00");
        QString strSwMinorVersion("00");
        QString strSWRevisionVersion("00");
        QString strSWYear("00");
        QString strSWMonth("00");
        QString strDay("00");

        // 将版本号的数字转换为字符串
        if(psImageHeader->u8MajorVersion < 10)
        {
            strSwMajorVersion = QString::number((int)0) + QString::number((int)psImageHeader->u8MajorVersion);
        }
        else
        {
            strSwMajorVersion = QString::number((int)psImageHeader->u8MajorVersion);
        }

        if(psImageHeader->u8MinorVersion < 10)
        {
             strSwMinorVersion =  QString::number((int)0) + QString::number((int)psImageHeader->u8MinorVersion);
        }
        else
        {
            strSwMinorVersion = QString::number((int)psImageHeader->u8MinorVersion);
        }

        if(psImageHeader->u8RevisionVersion < 10)
        {
             strSWRevisionVersion =  QString::number((int)0) + QString::number((int)psImageHeader->u8RevisionVersion);
        }
        else
        {
            strSWRevisionVersion = QString::number((int)psImageHeader->u8RevisionVersion);
        }

        strSWYear = QString::number((int)psImageHeader->u16ReleaseYear);

        if(psImageHeader->u8ReleaseMonth < 10)
        {
             strSWMonth =  QString::number((int)0) + QString::number((int)psImageHeader->u8ReleaseMonth);
        }
        else
        {
            strSWMonth = QString::number((int)psImageHeader->u8ReleaseMonth);
        }

        if(psImageHeader->u8ReleaseDay < 10)
        {
             strDay =  QString::number((int)0) + QString::number((int)psImageHeader->u8ReleaseDay);
        }
        else
        {
            strDay = QString::number((int)psImageHeader->u8ReleaseDay);
        }

        QString strSlaveSWVer = strSwMajorVersion + strSwMinorVersion + strSWRevisionVersion + strSWYear + strSWMonth + strDay;

        QTableWidgetItem *SlavSWLable =  new QTableWidgetItem(strSlaveSWLable);
        SlavSWLable->setTextAlignment(Qt::AlignCenter);// 居中对齐
        ui->SlaveTable->setItem(ui->SlaveTable->rowCount()-1,1,SlavSWLable);

        QTableWidgetItem *SlavSWVer =  new QTableWidgetItem(strSlaveSWVer);
        SlavSWVer->setTextAlignment(Qt::AlignCenter);// 居中对齐
        ui->SlaveTable->setItem(ui->SlaveTable->rowCount()-1,2,SlavSWVer);
    }

    // 禁止修改单元格
    ui->SlaveTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    // 设置整行选择
    ui->SlaveTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    //ui->SlaveTable->setSelectionMode(QAbstractItemView::SingleSelection);
    // Table显示
    ui->SlaveTable->show();

    // 更新子机数量
    this->u8SlaveNums++;
    ui->LineEdit_SlaveNums->setText(QString::number((int)this->u8SlaveNums));
}

// “打包”按键槽函数
void Widget::on_Btn_Packet_clicked()
{
    QString strMachineCode = ui->LineEdit_MachineCode->text();// 获取整机编码
    QString strMachineVer = ui->LineEdit_MachineVer->text();// 获取整机版本号
    U8  u8SlaveNums = (U8)ui->LineEdit_SlaveNums->text().toInt();// 获取子机数量

    QString strFilePath = ui->LineEdit_SavePath->text();// 获取要保存的文件目录

    ui->ProgressBar->setValue(0);// 进度条显示0
    // 检查整机编码是否合法
    if(!this->CheckMachineCodeIsLegal(strMachineCode))
    {
        // Critical消息框
        qDebug()<<"整机编码格式非法";
        this->MsgDlg->critical(this,
                               "整机编码格式非法",
                               "整机编码只能为数字、字母和下划线组合");
    }
    // 检查整机版本号是否合法
    else if(!this->CheckMachineVerIsLegal(strMachineVer))
    {
        // Critical消息框
        qDebug()<<"整机版本号格式非法";
        this->MsgDlg->critical(this,
                               "整机版本号格式非法",
                               "整机版本号只能为14个数字,如“01000020201226”");
    }
    else if(u8SlaveNums == 0)
    {
        // Critical消息框
        qDebug()<<"子机数量为0";
        this->MsgDlg->critical(this,
                               "子机数量非法",
                               "请添加需要打包的子机升级文件");
    }
    else if(!this->CheckSlaveTableIsLegal())
    {
        // Critical消息框
        qDebug()<<"子机信息非法";
        this->MsgDlg->critical(this,
                               "子机信息非法",
                               "请输入正确的子机软件标识符和版本号");
    }
    else if(strFilePath.isEmpty())
    {
        // Critical消息框
        qDebug()<<"保存文件目录为空";
        this->MsgDlg->critical(this,
                               "保存目录格式非法",
                               "请输入正确的保存位置");
    }
    else
    {
        ui->ProgressBar->setValue(60);// 进度条显示60
        // 创建包信息描述，整机+子机
        PackInfor_ts *psPackInfor = new PackInfor_ts[this->u8SlaveNums+1];
        // 创建子机文件数据缓存区，默认创建1MByte
        U8 *pu8SlaveFileBuff = new U8[1024*1024];
        U32 u32SlaveFileBuffLen = 0;
        // 子机偏移，其实偏移 = 48Byte * （子机数量+整机数量）
        U32 u32SlavePackOffSet = 48 * (this->u8SlaveNums+1);

        // 生成子机信息描述，并将全部子机数据读出到pu8SlaveFileBuff
        for(int i=1;i<=this->u8SlaveNums;i++)
        {
            // 获取子机标识符
            QString strSlaveSWLable = ui->SlaveTable->item(i-1,1)->text();
            strSlaveSWLable.resize(20,0x00);
            QByteArray baSlaveSWLable = strSlaveSWLable.toLatin1();
            memcpy((psPackInfor+i)->au8SWLable,baSlaveSWLable.data(),20);

            // 获取子机版本号
            QString strSlaveSWVer = ui->SlaveTable->item(i-1,2)->text();
            strSlaveSWVer.resize(14,0x00);
            QByteArray baSlaveSWVer = strSlaveSWVer.toLatin1();
            memcpy((psPackInfor+i)->au8SWVer,baSlaveSWVer.data(),14);

            // 子机固件偏移
            put_be_val(u32SlavePackOffSet,(psPackInfor+i)->au8OffSet,4);

            // 打开从机bin文件
            QFile SlaveFile_i((QString)this->SlavePathList.at(i-1));
            QDataStream SlaveFileOut_i(&SlaveFile_i);

            if(!SlaveFile_i.open(QIODevice::ReadOnly))
            {
                // 文件打开失败
                qDebug()<<"文件打开失败";
                this->MsgDlg->critical(this,
                                       "子机文件打开失败",
                                       "子机文件打开失败，请检查子机文件是否损坏");
                return ;
            }

            // 将从机文件读出
            U8 *pu8SlaveFileData = new U8[SlaveFile_i.size()];
            SlaveFileOut_i.readRawData((char*)pu8SlaveFileData,SlaveFile_i.size());

            // 从机文件写入缓冲区
            memcpy(pu8SlaveFileBuff + u32SlaveFileBuffLen,pu8SlaveFileData,SlaveFile_i.size());
            u32SlaveFileBuffLen += SlaveFile_i.size();

            // 子机固件偏移+上一个子机文件大小
            u32SlavePackOffSet += SlaveFile_i.size();
            // 子机固件长度
            put_be_val(SlaveFile_i.size(),(psPackInfor+i)->au8SWLen,4);

            // 子机固件CRC
            put_be_val(CRC_CalcValue(pu8SlaveFileData,SlaveFile_i.size(),u16CRC_SEED),
                       (psPackInfor+i)->au8SWCRC,
                       2);

            // 子机号
            (psPackInfor+i)->u8SlaveNum = i-1;

            // 预留字节为0
            memset((psPackInfor+i)->au8Res,0x00,8);

            // 信息CRC
            put_be_val(CRC_CalcValue((U8*)(psPackInfor+i),46,u16CRC_SEED),
                       (psPackInfor+i)->au8PackInforCRC,
                       2);

            // 打印子机信息
            print_arr(psPackInfor+i,48);
        }

        // 整机信息，获取整机编码
        QString strMachineSWLable = ui->LineEdit_MachineCode->text();
        strMachineSWLable.resize(20,0x00);
        QByteArray baSlaveSWLable = strMachineSWLable.toLatin1();
        memcpy((psPackInfor)->au8SWLable,baSlaveSWLable.data(),20);

        // 获取整机版本号
        QString strMachineSWVer = ui->LineEdit_MachineVer->text();
        strMachineSWVer.resize(14,0x00);
        QByteArray baMachineSWVer = strMachineSWVer.toLatin1();
        memcpy((psPackInfor)->au8SWVer,baMachineSWVer.data(),14);

        // 整机固件偏移0
        put_be_val(0,psPackInfor->au8OffSet,4);

        // 整机固件大小:子机文件总大小
        put_be_val(u32SlaveFileBuffLen,psPackInfor->au8SWLen,4);

        // 整机固件CRC,CRC 子机文件的全部数据
        U16 u16CRC = CRC_CalcValue((U8*)(pu8SlaveFileBuff),u32SlaveFileBuffLen,u16CRC_SEED);
        put_be_val(u16CRC,psPackInfor->au8SWCRC,2);

        // 子机数
        psPackInfor->u8SlaveNum = this->u8SlaveNums;

        // 预留字节为0
        memset(psPackInfor->au8Res,0x00,sizeof(psPackInfor->au8Res));

        // 整机信息包CRC校验
        put_be_val(CRC_CalcValue((U8*)(psPackInfor),46,u16CRC_SEED),psPackInfor->au8PackInforCRC,2);

        ui->ProgressBar->setValue(90);// 进度条显示90
        // 打印整机信息
        print_arr(psPackInfor,48);

        // 合成总文件
        QFile SaveFile(strFilePath);
        QDataStream SaveFileOut(&SaveFile);

        if(!SaveFile.open(QIODevice::WriteOnly))
        {
            // Critical消息框
            qDebug()<<"文件保存失败";
            this->MsgDlg->critical(this,
                                   "文件保存失败",
                                   "文件保存失败");
            return ;
        }

        SaveFileOut.writeRawData((const char*)psPackInfor,48*(this->u8SlaveNums+1));

        SaveFileOut.writeRawData((const char*)pu8SlaveFileBuff,u32SlaveFileBuffLen);

        SaveFile.close();
        ui->ProgressBar->setValue(100);// 进度条显示100
    }

//    qDebug()<<"整机编码："<<strMachineCode;
//    qDebug()<<"整机版本号："<<strMachineVer;
//    qDebug()<<"子机数量："<<u8SlaveNums;
//    qDebug()<<"保存位置："<<strFilePath;
//    qDebug()<<"文件名称："<<strFileName;
}
